package com.example.fireassistant_server.service.impl;

import com.example.fireassistant_server.dto.PointDto;
import com.example.fireassistant_server.dto.PointSearchFilter;
import com.example.fireassistant_server.dto.polygon.PolyGonCreateRequest;
import com.example.fireassistant_server.dto.polygon.PolyGonDto;
import com.example.fireassistant_server.dto.polygon.PolyGonUpdateRequest;
import com.example.fireassistant_server.entity.*;
import com.example.fireassistant_server.exception.BizException;
import com.example.fireassistant_server.exception.ExceptionType;
import com.example.fireassistant_server.mapper.PointMapper;
import com.example.fireassistant_server.mapper.PolyGonMapper;
import com.example.fireassistant_server.repository.PointRepository;
import com.example.fireassistant_server.repository.PolyGonPointRepository;
import com.example.fireassistant_server.repository.PolyGonRepository;
import com.example.fireassistant_server.repository.spec.PointSearchCriteria;
import com.example.fireassistant_server.repository.spec.PoloGonSpecifation;
import com.example.fireassistant_server.repository.spec.SearchOperation;
import com.example.fireassistant_server.service.FileService;
import com.example.fireassistant_server.service.PolyGonService;
import org.hibernate.Session;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.EntityManager;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicInteger;

@Service
public class PolyGonServiceImpl implements PolyGonService {

    private PolyGonMapper polyGonMapper;

    private PolyGonRepository polyGonRepository;

    private PointMapper pointMapper;

    private PointRepository pointRepository;

    private EntityManager entityManager;

    private FileService fileService;

    private PolyGonPointRepository polyGonPointRepository;
    @Override
    @Transactional
    public PolyGonDto addGon(PolyGonCreateRequest polyGonCreateRequest, User user) {
        PolyGon polyGon = new PolyGon();
        polyGon.setUserId(user.getId());
        if (polyGonCreateRequest.getTitle() != null) {
            polyGon.setTitle(polyGonCreateRequest.getTitle());
        }
        if (polyGonCreateRequest.getBackground_color() != null) {
            polyGon.setBackground_color(polyGonCreateRequest.getBackground_color());
        }
        if (polyGonCreateRequest.getDescription() != null) {
            polyGon.setDescription(polyGonCreateRequest.getDescription());
        }

        //存储标记点
        List<PointDto> points = polyGonCreateRequest.getPoints();
        BigDecimal sumLatitude = new BigDecimal(0);
        BigDecimal sumLongitude= new BigDecimal(0);
        for (PointDto pointDto : points) {
            sumLatitude = sumLatitude.add(pointDto.getLatitude());
            sumLongitude = sumLongitude.add(pointDto.getLongitude());
        }
        sumLatitude = sumLatitude.divide(BigDecimal.valueOf(points.size()), 12, RoundingMode.HALF_UP);
        sumLongitude = sumLongitude.divide(BigDecimal.valueOf(points.size()), 12, RoundingMode.HALF_UP);
        polyGon.setLatitude(sumLatitude);
        polyGon.setLongitude(sumLongitude);

        //存储中心点
        PolyGonDto polyGonDto=polyGonMapper.toDto(polyGonRepository.save(polyGon));
        //存储中间表
        points.forEach(item -> {
            //改造item使之成为point,并存储,存储时才会生成id
            Point point = pointMapper.createEntity(item);
            pointRepository.save(point);
            PolyGonPoint polyGonPoint = new PolyGonPoint();
            polyGonPoint.setPointId(point.getId());
            polyGonPoint.setPolygonId(polyGonDto.getId());
            polyGonPointRepository.save(polyGonPoint);
        });
        polyGonRepository.flush();
        entityManager.unwrap(Session.class).clear();
        return get(polyGonDto.getId());
    }

    @Override
    public PolyGonDto get(String id) {
        Optional<PolyGon> polyGon = polyGonRepository.findById(id);
        if (!polyGon.isPresent()) {
            throw new BizException(ExceptionType.POLYGON_NOT_FOUND);
        }
        return polyGonMapper.toDto(polyGon.get());
    }

    @Override
    @Transactional
    public void delete(String id) {
        Optional<PolyGon> polyGon = polyGonRepository.findById(id);
        if (!polyGon.isPresent()) {
            throw new BizException(ExceptionType.POLYGON_NOT_FOUND);
        }
        polyGonRepository.deleteById(id);
        // 先删除对应的file
        List<File> files=polyGon.get().getFiles();
        if (!files.isEmpty()){
            files.forEach(item->{
                fileService.delete("polyGon",item.getId());
            });
        }

        //删除其他点
        List<Point> points = polyGon.get().getPoints();
        if (!points.isEmpty()){
            points.forEach(item->{
                polyGonPointRepository.deletePolyGonPointByPointId(item.getId());
                pointRepository.deleteById(item.getId());
            });
        }

    }

    @Override
    public Page<PolyGonDto> search(PointSearchFilter pointSearchFilter) {
        PoloGonSpecifation specs = new PoloGonSpecifation();
        //添加所查询的latitude范围
        if (!Objects.equals(pointSearchFilter.getMinLatitude(), null) && !Objects.equals(pointSearchFilter.getMaxLatitude(), null)) {
            specs.add(new PointSearchCriteria("latitude", pointSearchFilter.getMinLatitude(), pointSearchFilter.getMaxLatitude(), SearchOperation.BETWEEN));
        }
        //添加所查询的longitude范围
        if (!Objects.equals(pointSearchFilter.getMinLongitude(), null) && !Objects.equals(pointSearchFilter.getMaxLongitude(), null)){
            specs.add(new PointSearchCriteria("longitude", pointSearchFilter.getMinLongitude(), pointSearchFilter.getMaxLongitude(), SearchOperation.BETWEEN));
        }
        return polyGonRepository.findAll(specs, pointSearchFilter.toPageable()).map(polyGonMapper::toDto);
    }

    @Override
    @Transactional
    public PolyGonDto update(String id, PolyGonUpdateRequest polyGonUpdateRequest) {

        Optional<PolyGon> polyGon = polyGonRepository.findById(id);
        if (!polyGon.isPresent()) {
            throw new BizException(ExceptionType.POLYGON_NOT_FOUND);
        }
        //新请求的点
        List<PointDto> newPoints = polyGonUpdateRequest.getPoints();

        //原来对应的点
        List<Point> points = polyGon.get().getPoints();

        BigDecimal sumLatitude = new BigDecimal(0);
        BigDecimal sumLongitude= new BigDecimal(0);
        for (PointDto pointDto : newPoints) {
            sumLatitude = sumLatitude.add(pointDto.getLatitude());
            sumLongitude = sumLongitude.add(pointDto.getLongitude());
        }
        sumLatitude = sumLatitude.divide(BigDecimal.valueOf(newPoints.size()), 12, RoundingMode.HALF_UP);
        sumLongitude = sumLongitude.divide(BigDecimal.valueOf(newPoints.size()), 12, RoundingMode.HALF_UP);
        polyGon.get().setLatitude(sumLatitude);
        polyGon.get().setLongitude(sumLongitude);

//         为line添加没有的点
        if (!newPoints.isEmpty()){
            newPoints.forEach(newPoint->{
                AtomicInteger count = new AtomicInteger(0);
                points.forEach(item->{
                    if (newPoint.getLatitude().compareTo(item.getLatitude())== 0 && newPoint.getLongitude().compareTo(item.getLongitude())==0){
                        count.getAndIncrement();
                    }
                });

                // 在points这种没有与之相同的点 需要添加
                if (count.get()==0){
                    // points中 没有 newPoint 添加newPoint
                    //改造item使之成为point,并存储,存储时才会生成id
                    Point point = pointMapper.createEntity(newPoint);
                    pointRepository.save(point);
                    PolyGonPoint polyGonPoint = new PolyGonPoint();
                    polyGonPoint.setPointId(point.getId());
                    polyGonPoint.setPolygonId(id);
                    polyGonPointRepository.save(polyGonPoint);
                }
            });
        }
        //删除之前的点
        if (!points.isEmpty()){
            points.forEach(item -> {
                AtomicInteger count = new AtomicInteger(0);
                newPoints.forEach(newPoint->{
                    if (newPoint.getLatitude().compareTo(item.getLatitude())== 0 && newPoint.getLongitude().compareTo(item.getLongitude())==0){
                        count.getAndIncrement();
                    }
                });
                // 在newPoints这种没有与之相同的点 需要删除
                if (count.get()==0){
                    polyGonPointRepository.deletePolyGonPointByPointId(item.getId());
                    pointRepository.deleteById(item.getId());
                }
            });
        }
        if (polyGonUpdateRequest.getTitle() != null) {
            polyGon.get().setTitle(polyGonUpdateRequest.getTitle());
        }
        if(polyGonUpdateRequest.getDescription()!= null){
            polyGon.get().setDescription(polyGonUpdateRequest.getDescription());
        }
        if (polyGonUpdateRequest.getBackground_color() != null) {
            polyGon.get().setBackground_color(polyGonUpdateRequest.getBackground_color());
        }

        // 解决缓存问题
        pointRepository.flush();
        entityManager.unwrap(Session.class).clear();
        return get(id);
    }


    @Autowired
    public void setPolyGonMapper(PolyGonMapper polyGonMapper) {
        this.polyGonMapper = polyGonMapper;
    }

    @Autowired
    public void setPolyGonRepository(PolyGonRepository polyGonRepository) {
        this.polyGonRepository = polyGonRepository;
    }

    @Autowired
    public void setPointMapper(PointMapper pointMapper) {
        this.pointMapper = pointMapper;
    }

    @Autowired
    public void setPointRepository(PointRepository pointRepository) {
        this.pointRepository = pointRepository;
    }

    @Autowired
    public void setPolyGonPointRepository(PolyGonPointRepository polyGonPointRepository) {
        this.polyGonPointRepository = polyGonPointRepository;
    }

    @Autowired
    public void setEntityManager(EntityManager entityManager) {
        this.entityManager = entityManager;
    }

    @Autowired
    public void setFileService(FileService fileService) {
        this.fileService = fileService;
    }
}
